package com.xdf.springboot.filter;


import com.sun.xml.internal.ws.client.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.annotation.Resource;

/**
 * 安全基线拦截器：
 * 防重放、防篡改
 */
@Component
public class CosSecurityFilter extends ZuulFilter {

    private final Logger log = LoggerFactory.getLogger(CosSecurityFilter.class);


    @Resource
    private CosSecurityProperties cosSecurityProperties;

    @Resource
    private CosSecurityUtil cosSecurityUtil;

    @Resource
    private AntPathMatcher antPathMatcher;

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /**
     * 拦截顺序，越小越优先
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return -9;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        if (BooleanUtil.isTrue(cosSecurityProperties.getEnable())&& !ctx.getBoolean("transmit")) {
            return true;
        } else {
            log.debug("【安全基线】未开启");
            return false;
        }
    }

    /**
     * 如果安全校验不通过，请求上下文中会有isSecurityPass；
     * isSecurityPass为true，代表安全校验通过
     * isSecurityPass为false，代表安全校验不通过
     */
    @Override
    public Object run() {
        log.info("---CosSecurityFilter---");
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String uri = request.getRequestURI();

        String sign = request.getHeader(CosSecurityConstants.HEADER_SIGN);
        String timestamp = request.getHeader(CosSecurityConstants.HEADER_TIMESTAMP);
        String nonce = request.getHeader(CosSecurityConstants.HEADER_NONCE);
        String clientVersion = request.getHeader(CosSecurityConstants.HEADER_VERSION);
        String method = request.getMethod();
        String contentType = request.getContentType();
        try {
            //url白名单
            List<String> ignoreUrlList = cosSecurityProperties.getIgnoreUrlList();
            if (CollectionUtil.isNotEmpty(ignoreUrlList)) {
                for (String ignoreUrl : ignoreUrlList) {
                    if (antPathMatcher.match(uri, ignoreUrl)) {
                        return null;
                    }
                }
            }
            switch (method) {
                case ServletUtil.METHOD_POST:

                    //application/json才校验签名
                    if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
                        // 版本号不符合条件
                        if (StrUtil.isNotEmpty(clientVersion) && (!Pattern.matches(CosSecurityConstants.PATTERN_VERSION, clientVersion)
                                || AppUtil.compareVersion(clientVersion, CosSecurityConstants.CONFIG_BASE_VERSION_VALUE) < 0)) {
                            if (cosSecurityProperties.getIsValidVersion()){
                                //如果开启版本号校验并且版本号为空或者不符合条件
                                throw new BusinessException(ExceptionEnum.VERSION_LOW.getCode(), ExceptionEnum.VERSION_LOW.getErrMsg());
                            } else {
                                //如果不开启版本号校验，并且版本在11以下 不验证签名
                                return null;
                            }
                        }
                        BodyReaderHttpServletRequestWrapper httpServletRequestWrapper = new BodyReaderHttpServletRequestWrapper(request);
                        //校验签名
                        cosSecurityUtil.validPostSign(sign, Long.valueOf(timestamp), nonce, httpServletRequestWrapper);
                        ctx.setRequest(httpServletRequestWrapper);
                        //校验超时时间
                        cosSecurityUtil.validTimestamp(timestamp);
                        //校验随机数
                        cosSecurityUtil.validAndSaveNonce(nonce, request);
                    }
                    break;
                case ServletUtil.METHOD_GET:
                    // GET请求不作处理
                    break;
                default:
                    break;
            }
            ctx.set(CosSecurityConstants.KEY_IS_SECURITY_PASS, true);
        } catch (BusinessException e) {
            log.error("【安全请求校验】校验不通过，uri=[{}],timestamp=[{}],nonce=[{}],sign=[{}],errorMessage=[{}]", uri, timestamp, nonce, sign, e.getMessage());
            log.error("error:",e);
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
            ResultMessage result = new ResultMessage(false, e.getCode(), e.getErrMsg());
            ctx.getResponse().setCharacterEncoding("UTF-8");
            ctx.getResponse().setContentType("application/json; charset=utf-8");
            ctx.setResponseBody(JSON.toJSONString(result, SerializerFeature.BrowserCompatible));
            ctx.set(CosSecurityConstants.KEY_IS_SECURITY_PASS, false);
        } catch (Exception e) {
            log.error("【安全请求校验】校验失败，uri=[{}],timestamp=[{}],nonce=[{}],sign=[{}],errorMessage=[{}]", uri, timestamp, nonce, sign, e.getMessage());
            log.error("error:",e);
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
            ResultMessage result = new ResultMessage(false, ExceptionEnum.SIGN_INVALID.getCode(), ExceptionEnum.SIGN_INVALID.getErrMsg());
            ctx.getResponse().setCharacterEncoding("UTF-8");
            ctx.getResponse().setContentType("application/json; charset=utf-8");
            ctx.setResponseBody(JSON.toJSONString(result, SerializerFeature.BrowserCompatible));
            ctx.set(CosSecurityConstants.KEY_IS_SECURITY_PASS, false);
        }
        return null;
    }

}